#include <stdio.h>
#include <time.h>

#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <arpa/inet.h>
#include <errno.h>

#include "common.h"
#include "change.h"

// 发送icmp报文
int send_echo_request(int sock, struct sockaddr_in *addr, int ident, int seq)
{
    // 创建icmp报文结构体
    struct ICMP_ECHO icmp;
    // 结构体数据清零
    bzero(&icmp, sizeof(icmp));

    // 填充头部
    icmp.type = 8;
    icmp.code = 0;
    icmp.ident = htons(ident);
    icmp.seq = htons(seq);

    // 填充时间戳信息
    gettimeofday(&(icmp.tv), NULL);

    // 计算并填充校验码
    icmp.checksum = htons(
        calculate_checksum((u_int16_t *)&icmp, sizeof(icmp)));

    // 发送报文
    int bytes = sendto(sock, &icmp, sizeof(icmp), 0,
                       (struct sockaddr *)addr, sizeof(*addr));

    if (bytes == -1)
    {
        return -1;
    }

    return 0;
}

// 接收icmp报文
int recv_echo_reply(int sock, struct sockaddr_in *addr)
{
    struct ICMP_ECHO icmp;
    struct IP_HEADER ip_header;
    bzero(&icmp, sizeof(icmp));
    char buff[128] = {0};
    int addr_len = 0;
    // 接受报文
    int bytes = recvfrom(sock, buff, sizeof(buff), 0, (struct sockaddr *)&addr, &addr_len);
    if (bytes == -1)
    {
        // normal return when timeout
        if (errno == EAGAIN || errno == EWOULDBLOCK)
        {
            return 0;
        }

        return -1;
    }

    // 获取接受时间戳信息
    struct timeval tv;
    gettimeofday(&tv, NULL);

    memcpy(&ip_header, buff, 20);
    // 提取报文icmp部分
    memcpy(&icmp, buff + 20, sizeof(buff) - 20);
    float usec_interval = calculate_time_interval(&(icmp.tv), &tv);
    // printf("the time is %d %d\n",icmp.tv.tv_sec,icmp.tv.tv_usec);
    // printf("the time is %d %d\n",tv.tv_sec,tv.tv_usec);
    // printf("the time interval is:%d\n",usec_interval);

    printf("%d bytes from %s:icmp_seq=%d time=%.3f ms\n", addr_len, inet_ntoa(ip_header.sour_ip), htons(icmp.seq), usec_interval);
    // for(int i=0;i<sizeof(icmp);i++)
    // {
    //     printf("%x\n",*((unsigned char*)&icmp+i));
    // }
}

int main(int argc, char *argv[])
{
    // 参数应包含目的IP
    if (argc < 2)
    {
        printf("%s don't have dest IP\n", argv[0]);
        return -1;
    }
    // 创建原始socket icmp协议：IPPROTO_ICMP
    int icmp_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    printf("the status of icmp_socket:%d\n", icmp_socket);

    // 目的地址
    struct sockaddr_in dest_addr;
    dest_addr.sin_family = AF_INET;
    dest_addr.sin_addr.s_addr = (inet_addr(argv[1]));
    dest_addr.sin_port = 0;

    // icmp报文信息
    struct ICMP_ECHO icmp_data;
    int counts = 4;
    for (int i = 0; i < counts; i++)
    {
        int status_send = send_echo_request(icmp_socket, &dest_addr, 1, i);

        // printf("the status of send is:%x\n",status_send);
        // printf("the dest addr is:%u\n",dest_addr.sin_addr.s_addr);
        // printf("%s\n",inet_ntoa(dest_addr.sin_addr));
        // printf("the pid is:%x\n",getpid());

        int status_recv = recv_echo_reply(icmp_socket, &dest_addr);
    }
    return 0;
}